#include "kmean.hpp"


Kmean::Kmean(std::vector<Point> points,std::vector<Point> centroids,int dimension){
    this->points = points;
    this->dimension = dimension;
    this->centroids = centroids;
    this->clusterIndex.resize(points.size());
    this->previousIndex = this->clusterIndex;


}
std::vector<int> Kmean::getClusters(){
    return clusterIndex;
}

int Kmean::getDimension(){
return dimension;
}

void Kmean::printCentroids(){
    std::cout<<"Centroids : \n";
    for (int i = 0 ; i <NUM_OF_CLUSTERS;i++){
        print(centroids[i]);
    }

}
void Kmean::doKmean(){
   do{
       previousIndex = clusterIndex;
       findClosestCentroids();
       calculateNewCentroids();
       numOfIterations++;
   }while(!converged());

}

bool Kmean::converged(){
    for(int i = 0 ; i < clusterIndex.size() ; i++){
        if(clusterIndex[i] != previousIndex[i]){
            return false;
        }
    }
    return true;
}

void Kmean::findClosestCentroids(){
    for(int i = 0 ; i < points.size() ; i++){
        double min = INFINITY;
        for (int j = 0 ; j < centroids.size() ; j++){
            double dist = calculateDistance(points[i],centroids[j]);
            if(dist < min){
                clusterIndex[i] = j ;
                min = dist;
            }

        }
    }
}

void Kmean::printClusters(){
	std::cout<<"Clusters : "<<std::endl;
	for(int i = 0 ; i < clusterIndex.size() ; i++){
		std::cout<<"Point["<<i<<"] = "<<clusterIndex[i]<<"\n";
    }
}

void Kmean::calculateNewCentroids(){
    std::vector<Point> clusteredPoints;
    for(int c = 0 ; c < NUM_OF_CLUSTERS ; c++){
        
        for(int i = 0 ; i <points.size() ; i++ ){
            if(clusterIndex[i] == c){
                clusteredPoints.push_back(points[i]);
            }
        }
        centroids[c].assign(calculateMean(clusteredPoints));
        clusteredPoints.clear();  
    }
}

double Kmean::calculateDistance(Point a, Point b){
    double dist = 0 ;
    if(a.size() != b.size()) {
        return dist;
    }

    for ( int i = 0 ; i < a.size() ; i++){
        dist += (a[i] - b[i]) * (a[i] - b[i]) ; 
    }
    return sqrt(dist);
}

Point Kmean::calculateMean(std::vector<Point> clusterdPoints){
    Point sum(clusterdPoints[0].getPoint());
    for(int i = 1 ; i < clusterdPoints.size() ; i++){
        sum += clusterdPoints[i];
    }
    return sum/clusterdPoints.size(); 
}
void Kmean::print(Point p){
    std::cout<<"<";
    for (int i = 0; i < p.size(); i++) {
        std::cout<<p[i]<<",";
    }
    std::cout<<">\n";
}

void Kmean::printPoints(){
    std::cout<<"Points : \n";
    for(int i = 0 ; i < points.size() ; i ++){
        print(points[i]);
    }
}

void Kmean::printPointAndCluster(Point p,int cluster){
    std::cout<<"<";
    for (int i = 0; i < p.size(); i++) {
        std::cout<<p[i]<<",";
    }
    std::cout<<">\t\t"<<cluster<<std::endl;
}

void Kmean::printClusteredPoints(){
    std::cout<<"Points \t Cluster\n";
    for (int j = 0; j < NUM_OF_CLUSTERS; j++) {
    for (int i = 0; i < clusterIndex.size(); i++) {
        
            if (clusterIndex[i] == j) {
                 printPointAndCluster(points[i],clusterIndex[i]);
            }
        }
       
    }
}
int Kmean::getNumOfIterations(){
    return numOfIterations;
}




